package cn.batch.daemon.loadBatch;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPFileFilter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.batch.daemon.Configuration;
import cn.batch.daemon.bean.TaskMissionBean;
import cn.batch.daemon.hibernate.bean.PbocSummary;
import cn.batch.daemon.jdbc.MessionTaskStore;
import cn.batch.daemon.service.IProcReportService;
import cn.batch.daemon.taskEnum.MissionSts;
import cn.batch.daemon.taskEnum.TaskMissionType;
import cn.batch.daemon.util.Assert;
import cn.batch.daemon.util.BuildPbocSummary;
import cn.batch.daemon.util.ConstantParameter;
import cn.batch.daemon.util.FtpUtil;
import cn.batch.daemon.util.UnZipFile;

/**
 * 解析人行征信数据并存储数据
 * 
 * @author xihn
 * 
 */
public class LoadProcReportListener implements Runnable {
	private static final Log logger = LogFactory.getLog(LoadProcReportListener.class);

	private Configuration config;
	private ApplicationContext ctx;
	private IProcReportService procReportService;

	

	private static final int DEFAULT_SLEEPTIME = 5000;

	private String batchDate = null; // 批次日期
	private String localPath = null; // 需要处理的目标目录路径

//	private int sleepTime;
	private int errorSleepTime; // 出现任务异常后,线程休眠时间

	private String urlpath; // 要监听的指定目录
	private String outFilePath; // 解压缩文件的指定目录
	private String errorFilePath; // 解析出错的文件存放目录
	private String transEndFlag; // 文件传输完毕标志

	// FTP 配置
	private String ip;
	private int port;
	private String username;
	private String password;
	private String remotePath;

	private boolean initialized = false;
	private int totalNumber = 0; // 解析文件总数
	private int errorNumber = 0; // 解析出错文件数
	private long threadRunningTime = 0;
	private String errorMsg="";

	/**
	 * @param config
	 */
	public LoadProcReportListener(Configuration config) {
		super();
		this.config = config;
	}

	/**
	 * 初始化
	 */
	public void initialize() {
		if (ctx == null) {
			ctx = new ClassPathXmlApplicationContext(new String[] { "applicationContext*.xml" });
		}

		procReportService = (IProcReportService) ctx.getBean("procReportService");

		// setup time arguments
//		sleepTime = config.getSetting(ConstantParameter.SLEEPTIME, DEFAULT_SLEEPTIME);
		errorSleepTime = config.getSetting(ConstantParameter.ERROR_SLEEPTIME, DEFAULT_SLEEPTIME);

		threadRunningTime = config.getSetting(ConstantParameter.THREAD_RUNNING_TIME, 0);

		urlpath = config.getSetting(ConstantParameter.SEARCH_URL, "").trim();
		outFilePath = config.getSetting(ConstantParameter.OUT_FILE_URL, "").trim();
		errorFilePath = config.getSetting(ConstantParameter.ERROR_FILE_URL, "").trim();

		transEndFlag = config.getSetting(ConstantParameter.TRANSFER_END_Flag, "endFlag").trim();

		// INIT FTP
		ip = config.getSetting(ConstantParameter.PROC_FTP_IP, "").trim();
		port = config.getSetting(ConstantParameter.PROC_FTP_PORT, 21);
		username = config.getSetting(ConstantParameter.PROC_FTP_USERNAME, "").trim();
		password = config.getSetting(ConstantParameter.PROC_FTP_PASSWORD, "").trim();
		remotePath = config.getSetting(ConstantParameter.PROC_FTP_REMOTEPATH, "").trim();

		// 如果本地临时目录不存在，则创建。
		File localProcFile = new File(urlpath);
		File localTempDir = new File(outFilePath);
		File localErrorDir = new File(errorFilePath);
		if (!localTempDir.exists() || !localProcFile.exists() || !localErrorDir.exists()) {
			localTempDir.mkdirs();
			localProcFile.mkdirs();
			localErrorDir.mkdirs();
		}
		initialized = true;
	}

	public void run() {
		initialize();
		Assert.state(initialized, "必须初始化完成才能运行");

		boolean isDownloadSuccess = false;
		TaskMissionBean tmb = new TaskMissionBean();
		long threadStartupTime = System.currentTimeMillis();
		
		tmb.setBatch_no(batchDate);
		tmb.setMissionTyp(TaskMissionType.PRCL);
		// 更新任务状态表
		tmb.setDisposeStartTm(new Timestamp(System.currentTimeMillis())); // 起始时间
		tmb.setMissionSts(MissionSts.DISPOSING); // 任务状态
		tmb.setRemark("开始装载人行征信数据，请等待。"); // 备注

		try {
			MessionTaskStore.updateTaskMissionStatus(tmb);
			
			localPath = urlpath + batchDate + "/"; // 下载到本地目录
			errorFilePath = errorFilePath + batchDate + "/";
			while (true) {
				try {
					isDownloadSuccess = FtpUtil.downloadFile(ip, port, username,
							password, remotePath + batchDate + "/", localPath,
							transEndFlag, new FTPFileFilter() {
								
								@Override
								public boolean accept(FTPFile arg0) {
									String fname = arg0.getName().toLowerCase();
									return !fname.contains("eds") && fname.endsWith(".zip") || transEndFlag.equals(fname) ;
								}
							});
				} catch (Exception e) {
					logger.error(e);
				}
				if (!isDownloadSuccess) {
					if (System.currentTimeMillis() - threadStartupTime < threadRunningTime) {
						sleep(errorSleepTime);
						logger.warn("线程休眠后，继续运行。");
						continue;
					} else {
						errorMsg = "未获取到人行征信文件。";
						throw new RuntimeException(errorMsg);
					}
				} else {
					break;
				}
			} // while end
			
			

			// 开始处理人行征信文件
			handelDir(new File(localPath));

			// 如果有解析失败的文件，则将任务状态置为失败
			if (errorNumber > 0) {
				errorMsg = "解析文件总数：" + totalNumber +"失败：" + errorNumber;
				throw new RuntimeException(errorMsg);
			}
			
			// 更新任务状态表
			tmb.setDisposeEndTm(new Timestamp(System.currentTimeMillis()));
			tmb.setMissionSts(MissionSts.SUCCESS); // 任务状态
			String s = "结束装载征信数据。文件总数：" + totalNumber + "，成功："+(totalNumber-errorNumber)+"，失败："
			+ errorNumber;
			tmb.setRemark(s); // 备注
			MessionTaskStore.updateTaskMissionStatus(tmb);

			logger.info(s);
			
		} catch (Exception e) {
			
			// 更新任务状态表
			tmb.setDisposeEndTm(new Timestamp(System.currentTimeMillis()));
			tmb.setMissionSts(MissionSts.FAILED);
			tmb.setRemark("按天装载征信数据出错，请联系系统管理员解决问题。"+errorMsg);
			try {
				MessionTaskStore.updateTaskMissionStatus(tmb);
			} catch (SQLException sqle) {
				logger.error("更新任务状态或标识表失败。", sqle);
			}

			logger.error("按天装载征信数据出错。", e);
			throw new RuntimeException(e);
			// sleep(errorSleepTime);
		}

	} // run end


	/**
	 * 下载文件目录处理。procFile/YYYYMMDD/
	 */
	private void handelDir(File dir) {

		logger.info("开始处理人行征信文件于：" + new java.util.Date());
		long startTime = System.currentTimeMillis();
		List<File> errorFileList = new ArrayList<File>();
		File[] procFiles = dir.listFiles();
		for (int i = 0; i < procFiles.length; i++) {
			try {
				errorFileList.addAll(loadProcFile(procFiles[i]));
			} catch (IOException e) {
				logger.error("解析人行征信文件失败。", e);
			}
		}
		
		// 保存错误文件到指定目录
		errorNumber = errorFileList.size();
		if ( errorNumber > 0 ){
			saveErrorFile(errorFileList);
		}
		
		long along = System.currentTimeMillis() - startTime;
		logger.info("结束处理人行征信文件于：" + new java.util.Date() + ", 共耗时" + along
				+ " 毫秒");
	}

	/**
	 * 文件处理。
	 * @param file
	 * @return 解析出错的文件列表
	 * @throws IOException
	 */
	private List<File> loadProcFile(File file) throws IOException {
		List<File> errorFileList = new ArrayList<File>();
		String outDirPath = outFilePath + batchDate + File.separator;
		File outDirFile = new File(outDirPath);
		if (!outDirFile.exists()) {
			outDirFile.mkdirs();
		}

		String fileName = file.getName().toLowerCase();
		
		// 如果是人行征信ZIP文件
		if (fileName.endsWith(".zip") && !fileName.contains("eds")) {
			logger.info("开始处理"+fileName+"文件。");
			// 将文件解压缩
			UnZipFile.unZipFiles(file, outDirPath);

			// 解压完毕,遍历文件,解析XML文件并存储
			File xmlDir = new File(outDirPath);
			File[] allDirs = xmlDir.listFiles(new FileFilter() {

				@Override
				public boolean accept(File pathname) {
					return pathname.getName().endsWith(".xml");
				}
			});
			totalNumber += allDirs.length;
			
			for (int i = 0; i < allDirs.length; i++) {
				// 保存数据
				try {
					//增加参数fileName(解析文件名称)解决空征信报告问题 HLRain 20130829
					savePbocSummary(allDirs[i],allDirs[i].getName());
				} catch (Exception e) {
					logger.error("解析人行征信XML文件时出错。文件名: " + allDirs[i].getName(),
							e);
					// 记录解析出错的文件
					errorFileList.add(allDirs[i]);
				}
			}
			logger.info("结束处理"+fileName+"文件。");
		}
		
		return errorFileList;
	}

	private void saveErrorFile(List<File> errorFileList) {
		File errorFileDir = new File(errorFilePath);
		if (!errorFileDir.exists()) {
			errorFileDir.mkdirs();
		}
		
		try {
			// 压缩文件
//			ZipFile.ZipFiles(new File(errorFilePath + "ParseErrorFiles.zip"),
//					"", errorFileList.toArray(new File[errorFileList.size()]));
			for (File f : errorFileList) {
				String name = f.getName();
				int index = name.lastIndexOf("\\");
				if (index > 0) {
					name = name.substring(index + 1);
				}
				FileOutputStream fos = new FileOutputStream(errorFilePath + name);
				FileInputStream fis = new FileInputStream(f);
				
				byte[] b = new byte[4096];
				int length = 0;
				while ((length = fis.read(b)) != -1) {
					fos.write(b, 0, length);
				}
				fis.close();
				fos.close();
			}
			
		} catch (Exception e) {
			logger.error("保存解析出错文件异常。", e);
		}
	}

	private void savePbocSummary(File xmlFile,String fileName) throws Exception {
		PbocSummary pbocSummary = BuildPbocSummary.bulidPbocSummary(xmlFile, batchDate,fileName);
		PbocSummary qp = new PbocSummary();
		qp.setName(pbocSummary.getName());
		qp.setCertno(pbocSummary.getCertno());
		qp.setCerttype(pbocSummary.getCerttype());
		List<PbocSummary> list = procReportService.queryPbocSummary(qp);
		if (list.size() > 0) {
			for (PbocSummary pbocSummary2 : list) {
				procReportService.deletePbocSummary(pbocSummary2);
				logger.info(new StringBuilder("Delete Repeating Data: ")
						.append(pbocSummary2.getQuerytime()).append(", ")
						.append(pbocSummary2.getName()).append(", ")
						.append(pbocSummary2.getCertno()).append(", ")
						.append(pbocSummary2.getCerttype())
						.toString());
			}
		}
		procReportService.savePbocSummary(pbocSummary);
	}

	protected void sleep(long millis) {
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			logger.warn("Thread sleep fail:", e);
		}
	}

	public void setBatchDate(String batchDate) {
		this.batchDate = batchDate;
	}

}
